from market.view import View
from market.models import Asset, Player, BetRoom
from market.models import Strategy, AssetPercentageInStrategy, Fund 
from market.mathView import view2vec
from market.playerData import PlayerData
from market.marketData import MarketData
from market.strategyAndFundHelper import isStrategyValid, getBankAccount, getPercOnAssetInStrat
from django.db.models import F


#######################################################################################
# pays dividend and returns fund MtM
def payDividendAndReturnFundMtM( assetToKill, oldMarket, fund, bankAccount, percentageToPay ):
        # compute the dividend
        assetPercInStrat = getPercOnAssetInStrat(fund.strategy,assetToKill)
        dividend = percentageToPay * fund.numberOfAssetsInFund * assetPercInStrat * oldMarket.getAssetPrice( assetToKill )
        #if the dividend is not null, we pay it to the bank account
        if dividend != 0:
                #get the player bank account
                try:
                        playerBankAccount = Fund.objects.get( player = fund.player, strategy = bankAccount )
                         # add the dividend
                        if playerBankAccount.fundTargetPercentageInPortfolio == 0:
                                playerBankAccount.delete()
                                raise Fund.DoesNotExist
			playerBankAccount.numberOfAssetsInFund += dividend
			playerBankAccount.save()                 
                except Fund.DoesNotExist:
                        #remove 1% of the MtM from the main strat. This could lead to only 49% invested in the main strat
                        mainFund = Fund.objects.get( player = fund.player, strategy = fund.player.mainStrategy )
                        mainFund.fundTargetPercentageInPortfolio -= 0.01
                        mainFund.save()
                        #create a bank account with 1% of the MtM
                        playerBankAccount = Fund(
                                strategy = bankAccount,
                                player = fund.player,
                                fundTargetPercentageInPortfolio = 0.01,
                                numberOfAssetsInFund = dividend
                                )
			playerBankAccount.save()

        return fund.numberOfAssetsInFund * oldMarket.getStrategyMtM(fund.strategy)



#######################################################################################
# create a new strategy removing the asset from the current one. If we are trying to supress the last asset, it only suspends the transactions
def uninvestAssetAndMakeNewStrat( assetToKill, strat, oldMarketView ):
        
        allRemainningAssets = Asset.objects.filter( isAvailableForStandardStrategy = True, betroom = assetToKill.betroom )
        allRemainningAssetsList = [ a for a in allRemainningAssets]        
        allRemainningAssetsList.remove(assetToKill)# raise if not asset does not exist, let it throw 
        # if there is no assset, we should have prevented this before 
        if len(allRemainningAssetsList) == 0:
                suspendTransactions()
                return False
                                        
        tolerance = 0.0000001
        # we first check that the strategy is invested un the asset
        percentageList = AssetPercentageInStrategy.objects.filter( strategy = strat, asset = assetToKill )
        if len( percentageList ) > 0:
                # record the old percentage before deleting it
                oldViewOnAsset = getPercOnAssetInStrat( strat, assetToKill )
                percentageList[0].delete()
                percentageList = AssetPercentageInStrategy.objects.filter( strategy = strat )
                #if the strategy was not fully invest on the asset we are killing
                if abs( oldViewOnAsset - 1) > tolerance:
                        for p in percentageList:
                                p.assetPercentageInStrategy = p.assetPercentageInStrategy / ( 1. - oldViewOnAsset )
                                p.save()
                else:
                        # we delete the percentage (they should all be zero)
                        for p in percentageList:
                                if abs( p.assetPercentageInStrategy ) > tolerance:
                                        raise Exception("deleting a non zero percentage")
                                p.delete()
                                
                        #create as for the market
                        oldMktViewOnAsset0 = oldMarketView.getViewOnAsset( assetToKill )
                        if oldMktViewOnAsset0 == 1: #if the market was fully invested in this asset
                                # take the first asset and put everithing on it
                                inheritingAsset = allRemainningAssetsList[ 0 ]#this exists, otherwise we would have returned already
                                
                                raise Exception("The system does not support the suppression of an asset fully invesed in all the strategies")
                        else:#if the market was not fully invested in this asset
                                count = 0
                                for a in oldMarketView.getFullView(): 
                                        if a[0] != assetToKill:
                                                count = count + 1
                                                p = AssetPercentageInStrategy(
                                                        asset = a[0],
                                                        strategy = strat,
                                                        assetPercentageInStrategy = a[1] / ( 1. - oldMktViewOnAsset0 ) )
                                                p.save()
                                if count == 0:
                                        raise Exception("no percentage has been added to the straegy")
        #end
        return True



#######################################################################################
# checks that the asset is not invested at all and kill it
def definitivelyKillAsset( assetToKill ):
        if not( assetToKill.isAvailableForStandardStrategy ):
                raise Exception("cannot kill %s. It is not an availlable asset." %assetToKill.name )
        allRemainningAssets = Asset.objects.filter( isAvailableForStandardStrategy = True, betroom = assetToKill.betroom )
        if len(allRemainningAssets) == 1:
                assetToKill.isAvailableForStandardStrategy = False
                suspendTransaction()
        else:
                percentatgeList = AssetPercentageInStrategy.objects.filter( asset = assetToKill )
                if len( percentatgeList ) != 0:
                        raise Exception("the asset in not fully desinvested when it should. Something went badly wong. You probably will have to recreate the data base")
                # we DO NOT uncomment the next line because of the history, instead we make it unavaiable for investement
                #assetToKill.delete()
                assetToKill.isAvailableForStandardStrategy = False
                assetToKill.save()
        return

        
#######################################################################################
# kills the asset in the glory way. This means the asset value is doubled:
# - it pays a dividend to the player bank accout (opening one if necessary)
# - the value is also reinvested in the remaining strategy
def killAssetWithConstantMtM( assetToKill, percentageToPay ):
        assetList = Asset.objects.filter(betroom=assetToKill.betroom)
        if not ( assetToKill in assetList ):
                raise  Exception(assetToKill.name + " is not an asset")

        # get the bank account
        bankAccount = getBankAccount(assetToKill.betroom)
        #create the old market
        oldMarket = MarketData(assetToKill.betroom)
        oldMarketView = oldMarket.getMarketView()

        # The system does not support the suppression of an asset fully invesed in all the strategies
        # if someone try to do this, it suspend the market
        if oldMarket.getMarketExpectation(assetToKill)==1:
                return 0
                
                
        #loop over the founds, add the dividend and register fund MtM
        fundList = Fund.objects.filter(strategy__betroom=assetToKill.betroom)
        fundListAndMtM = []
        for f in fundList:
		tmp = isStrategyValid( f.strategy )
		if not ( tmp[0] ):
                        raise Exception( tmp[1] )
                if f.strategy != bankAccount:
                        #register fund MtM calling a function wich pays the dividend
                        fundListAndMtM.append( [ f, payDividendAndReturnFundMtM( assetToKill, oldMarket, f, bankAccount, percentageToPay ) ] )
                        
              
        # kill asset and create new strategies without this asset
        continue0 = True
        stratList = Strategy.objects.filter(betroom=assetToKill.betroom)
        for strat in stratList:
                if strat != bankAccount and continue0:
                        continue0 = uninvestAssetAndMakeNewStrat( assetToKill, strat, oldMarketView )

        
        if continue0:
                #at this point, all strategie shoulf be uninvested on the asset to kill.
                definitivelyKillAsset( assetToKill )
        
                # create the new market
                newMarket = MarketData(assetToKill.betroom)
                # adjust the number of asset such that the fund has a constant MtM
                for fundAndMtM in fundListAndMtM:
                        # next line is strange : as we already have performed a .save() on the Funds, it will overwrite it.
                        # we do not want to do fund = fundAndMtM[0]
                        fund =  Fund.objects.get( player = fundAndMtM[0].player, strategy = fundAndMtM[0].strategy )
                        oldMtM =  fundAndMtM[1]
                        stratNewMtM = newMarket.getStrategyMtM(fund.strategy)
                        if stratNewMtM != 0:
                                fund.numberOfAssetsInFund =  oldMtM / stratNewMtM
                        else:
                                fund.numberOfAssetsInFund = 0
                        fund.save()

        #end
        return 1

#######################################################################################
# kills the asset in the shame way. This means the asset value is suppressed:
# only ne number of surviving assset remain constant. Strategies who are not invested in the killed asset
# are rewarded because the prices of remaining asset increase.
def killAssetWithConstantNumbers( assetToKill ):
        assetList = Asset.objects.filter(betroom=assetToKill.betroom)
        if not ( assetToKill in assetList ):
                raise  Exception(assetToKill.name + " is not an asset")
        
        #create the old market
        oldMarket = MarketData(assetToKill.betroom)
        oldMarketView = oldMarket.getMarketView()

        # get the bank account
        bankAccount = getBankAccount(assetToKill.betroom)
        
        #loop over the founds, add the dividend and register fund MtM
        fundList = Fund.objects.filter(strategy__betroom=assetToKill.betroom)
        for fund in fundList:
		tmp = isStrategyValid( fund.strategy )
		if not ( tmp[0] ):
                        raise Exception( tmp[1] )
                if fund.strategy != bankAccount:
                        # compute the new number of assets
                        oldTotalNumberOfAsset = fund.numberOfAssetsInFund
                        oldNumberOfAsset = getPercOnAssetInStrat(fund.strategy,assetToKill) * oldTotalNumberOfAsset
                        #register fund number of asset in the fund
                        fund.numberOfAssetsInFund =  oldTotalNumberOfAsset - oldNumberOfAsset
                        fund.save()

                        
        # kill asset and create new strategies without this asset
        stratList = Strategy.objects.filter(betroom=assetToKill.betroom)
        for strat in stratList:
                if strat != bankAccount:
                        uninvestAssetAndMakeNewStrat( assetToKill, strat, oldMarketView )
                        
        #at this point, all strategie shoulf be uninvested on the asset to kill.
        definitivelyKillAsset( assetToKill )
        
        #end
        return
               
                
